import {Entity} from '../entity/entity';
import {StringUtils} from './string-utils';
import {EntityRef} from '../entity/entity-ref';
import format from 'date-fns/format';

export class EntityUtils {
    static floatTypes = new Set([
        'float', 'Float', 'java.lang.Float',
        'double', 'Double', 'java.lang.Double'
    ]);

    static digitsTypes = new Set([
        'int', 'Integer', 'java.lang.Integer',
        'long', 'Long', 'java.lang.Long'
    ]);

    static TYPE_DEFAULT_VALUES = {
        'java.lang.String': '',
        'java.lang.Long': 0,
        'Long': 0,
        'long': 0,
        'java.lang.Integer': 0,
        'Integer': 0,
        'int': 0,
        'java.lang.Double': 0.0,
        'Double': 0.0,
        'double': 0.0,
        'java.lang.Float': 0.0,
        'Float': 0.0,
        'float': 0.0,
        'java.math.BigDecimal': 0.0,
        'BigDecimal': 0.0,
        'java.lang.Boolean': true,
        'Boolean': true,
        'boolean': true,
        'java.lang.Byte': '',
        'byte': '',
        'java.lang.Character': '',
        'Character': '',
        'char': '',
        'Date': `"${format(new Date(), 'yyyy-MM-dd HH:mm:ss')}"`,
    };

    public static getBaseTypeDefaultValue(typeName: string): string {
        return EntityUtils.TYPE_DEFAULT_VALUES[typeName.toString()];
    }

    public static isPrimitive(entity: Entity): boolean {
        if (!entity) {
            return false;
        }
        const entityName = entity.name;
        return (entity.primitive && entity.primitive === true) || StringUtils.isPrimitiveName(entityName);
    }

    public static clone(obj) {
        if (!obj || 'object' !== typeof obj) {
            return obj;
        }
        const copy = obj.constructor();
        for (const attr in obj) {
            if (obj.hasOwnProperty(attr)) {
                copy[attr] = EntityUtils.clone(obj[attr]);
            }
        }
        return copy;
    }

    public static getParamPattern(param: EntityRef): RegExp {
        if (EntityUtils.isDigits(param.entityName)) {
            // 如果是整数
            if (param.required) {
                return new RegExp('^-?\\d+$');
            } else {
                return new RegExp('^$|^-?\\d+$');
            }
        }

        if (EntityUtils.isFloat(param.entityName)) {
            // 如果是浮点型
            if (param.required) {
                return new RegExp('^-?\\d+(\\.\\d+)?$');
            } else {
                return new RegExp('^$|^-?\\d+(\\.\\d+)?$');
            }
        }

        if (param.required) {
            return new RegExp('^.+$');
        }
        return null;
    }

    /**
     * 查询当前 entity的数据类型： 0=基本类型 1=枚举 2=pojo 3=collection 4=map 5=数组
     * @param entity
     */
    public static getEntityType(entity: Entity): number {
        if (!entity) {
            return 0;
        }

        if (entity.array) {
            // 数组
            return 5;
        } else if (entity.collection) {
            // 集合
            return 3;
        } else if (entity.enumerate) {
            return 1;
        } else if (entity.map) {
            return 4;
            // } else if (entity.fields && entity.fields.length > 0) {
            //     return 2;
        } else if (entity.primitive || EntityUtils.isPrimitive(entity)) {
            return 0;
        } else {
            return 2;
        }
    }

    /**
     * 是否整数
     * @param {string} typeName
     * @returns {boolean}
     */
    private static isDigits(typeName: string): boolean {
        return EntityUtils.digitsTypes.has(typeName);
    }

    private static isFloat(typeName: string): boolean {
        return EntityUtils.floatTypes.has(typeName);
    }
}

export const deepCopy = <T>(target: T): T => {
    if (target === null) {
        return target;
    }
    if (target instanceof Date) {
        return new Date(target.getTime()) as any;
    }
    if (target instanceof Array) {
        const cp = [] as any[];
        (target as any[]).forEach((v) => {
            cp.push(v);
        });
        return cp.map((n: any) => deepCopy<any>(n)) as any;
    }
    if (typeof target === 'object' && target !== {}) {
        const cp = {...(target as { [key: string]: any })} as { [key: string]: any };
        Object.keys(cp).forEach(k => {
            cp[k] = deepCopy<any>(cp[k]);
        });
        return cp as T;
    }
    return target;
};

